
/* ------------------
	 Server
	 usage: java Server [RTSP listening port]
	 ---------------------- */


import java.io.*;
import java.net.*;
import java.awt.*;
import java.util.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.Timer;

public class Server extends JFrame implements ActionListener {
	
	// Server config
	// serverType = 'root' or 'reg'
	private String serverType;
	private String parentServer;
	private String lastUsedVideo;
	private String localHostName;
	
	static ServerSocket fileRequestSocket;
	//RTP variables:
	//----------------
	DatagramSocket RTPsocket; //socket to be used to send and receive UDP packets
	DatagramPacket senddp; //UDP packet containing the video frames

	InetAddress ClientIPAddr; //Client IP address
	int RTP_dest_port = 0; //destination port for RTP packets	(given by the RTSP Client)
	
	//GUI:
	//----------------
	JLabel label;

	//Video variables:
	//----------------
	int imagenb = 0; //image nb of the image currently transmitted
	VideoStream video; //VideoStream object used to access video frames
	static int MJPEG_TYPE = 26; //RTP payload type for MJPEG video
	static int FRAME_PERIOD = 100; //Frame period of the video to stream, in ms
	static int VIDEO_LENGTH = 500; //length of the video in frames

	Timer timer; //timer used to send the images at the video frame rate
	byte[] buf; //buffer used to store the images to send to the client 

	//RTSP variables
	//----------------
	//rtsp states
	final static int INIT = 0;
	final static int READY = 1;
	final static int PLAYING = 2;
	//rtsp message types
	final static int SETUP = 3;
	final static int PLAY = 4;
	final static int PAUSE = 5;
	final static int TEARDOWN = 6;

	static int state; //RTSP Server state == INIT or READY or PLAY
	Socket RTSPsocket; //socket used to send/receive RTSP messages
	//input and output stream filters
	static BufferedReader RTSPBufferedReader;
	static BufferedWriter RTSPBufferedWriter;
	static String VideoFileName; //video file requested from the client
	static int RTSP_ID = 123456; //ID of the RTSP session
	int RTSPSeqNb = 0; //Sequence number of RTSP messages within the session
	
	final static String CRLF = "\r\n";

	//--------------------------------
	//Constructor
	//--------------------------------
	public Server(){

		//init Frame
		super("Server");

		//init Timer
		timer = new Timer(FRAME_PERIOD, this);
		timer.setInitialDelay(0);
		timer.setCoalesce(true);

		//allocate memory for the sending buffer
		buf = new byte[15000]; 

		//Handler to close the main window
		addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
	//stop the timer and exit
	timer.stop();
	System.exit(0);
			}});

		//GUI:
		label = new JLabel("Send frame #				", JLabel.CENTER);
		getContentPane().add(label, BorderLayout.CENTER);
	}
					

	//------------------------
	//Handler for timer
	//------------------------
	public void actionPerformed(ActionEvent e) {

		//if the current image nb is less than the length of the video
		if (imagenb < VIDEO_LENGTH)
			{
	//update current imagenb
	imagenb++;
			 
	try {
		//get next frame to send from the video, as well as its size
		int image_length = video.getnextframe(buf);

		//Builds an RTPpacket object containing the frame
		RTPpacket rtp_packet = new RTPpacket(MJPEG_TYPE, imagenb, imagenb*FRAME_PERIOD, buf, image_length);
		
		//get to total length of the full rtp packet to send
		int packet_length = rtp_packet.getlength();

		//retrieve the packet bitstream and store it in an array of bytes
		byte[] packet_bits = new byte[packet_length];
		rtp_packet.getpacket(packet_bits);

		//send the packet as a DatagramPacket over the UDP socket 
		senddp = new DatagramPacket(packet_bits, packet_length, ClientIPAddr, RTP_dest_port);
		RTPsocket.send(senddp);

		//System.out.println("Send frame #"+imagenb);
		//print the header bitstream
		rtp_packet.printheader();

		//update GUI
		label.setText("Send frame #" + imagenb);
	}
	catch(Exception ex)
		{
			System.out.println("Exception caught: "+ex);
			System.exit(0);
		}
			}
		else
			{
	//if we have reached the end of the video file, stop the timer
	timer.stop();
			}
	}

	//------------------------------------
	//Parse RTSP Request
	//------------------------------------
	private int parse_RTSP_request()
	{
		int request_type = -1;
		try{
			//parse request line and extract the request_type:
			String RequestLine = RTSPBufferedReader.readLine();
			//System.out.println("RTSP Server - Received from Client:");
			System.out.println(RequestLine);

			StringTokenizer tokens = new StringTokenizer(RequestLine);
			String request_type_string = tokens.nextToken();

			//convert to request_type structure:
			if ((new String(request_type_string)).compareTo("SETUP") == 0)
	request_type = SETUP;
			else if ((new String(request_type_string)).compareTo("PLAY") == 0)
	request_type = PLAY;
			else if ((new String(request_type_string)).compareTo("PAUSE") == 0)
	request_type = PAUSE;
			else if ((new String(request_type_string)).compareTo("TEARDOWN") == 0)
	request_type = TEARDOWN;

			if (request_type == SETUP)
	{
		//extract VideoFileName from RequestLine
		VideoFileName = tokens.nextToken();
		File videoFile = new File(localHostName +"/"+VideoFileName);
		System.out.println("Last file used is " + lastUsedVideo);
		System.out.println("VideoFile exists: " + videoFile.exists());

        requestFile(VideoFileName);
        
        lastUsedVideo = VideoFileName;
	}
			
			

			//parse the SeqNumLine and extract CSeq field
			String SeqNumLine = RTSPBufferedReader.readLine();
			System.out.println("Seqnumber " + SeqNumLine);
			tokens = new StringTokenizer(SeqNumLine);
			tokens.nextToken();

			RTSPSeqNb = Integer.parseInt(tokens.nextToken());

			//get LastLine
			String LastLine = RTSPBufferedReader.readLine();


			if (request_type == SETUP){
				//extract RTP_dest_port from LastLine
				tokens = new StringTokenizer(LastLine);
				for (int i=0; i<3; i++)
					tokens.nextToken(); //skip unused stuff
				RTP_dest_port = Integer.parseInt(tokens.nextToken());

			}
			//else LastLine will be the SessionId line ... do not check for now.
		}
		catch(Exception ex)
			{
	System.out.println("Exception caught in setup: ");
	ex.printStackTrace();
	System.exit(0);
			}
		return(request_type);
	}

	//------------------------------------
	//Send RTSP Response
	//------------------------------------
	private void send_RTSP_response()
	{
		try{
			RTSPBufferedWriter.write("RTSP/1.0 200 OK"+CRLF);
			RTSPBufferedWriter.write("CSeq: "+RTSPSeqNb+CRLF);
			RTSPBufferedWriter.write("Session: "+RTSP_ID+CRLF);
			RTSPBufferedWriter.flush();
			//System.out.println("RTSP Server - Sent response to Client.");
		}
		catch(Exception ex)
			{
	System.out.println("Exception caught: "+ex.getStackTrace());
	System.exit(0);
			}
	}

	
	public void sendFile(Socket sendSocket, String filename) throws IOException {

		String fileString = localHostName+"/"+filename;
		System.out.println("Sending  file " + fileString + " on socket " + sendSocket.getInetAddress().getHostAddress());
		File myFile = new File (fileString);
		
		//Send further up the tree
		if(!myFile.exists()) {
			System.out.println("File not found, sends request to parent");
			requestFile(filename);
			myFile = new File(fileString);
		}

		int filelength = (int)myFile.length();
		
		// sends the filelength to the requester
		PrintWriter socketOutput = new PrintWriter(new BufferedWriter(new OutputStreamWriter(sendSocket.getOutputStream())), true);
		socketOutput.println(filelength);
		//socketOutput.close();
		  
		byte [] mybytearray  = new byte [(int)myFile.length()];
		FileInputStream fis = new FileInputStream(myFile);
		BufferedInputStream bis = new BufferedInputStream(fis);
		bis.read(mybytearray,0,mybytearray.length);
		OutputStream os = sendSocket.getOutputStream();
		System.out.println("Sending " + filename + " to " + sendSocket.getInetAddress().getHostName());
		os.write(mybytearray,0,mybytearray.length);
		os.flush();
		sendSocket.close();
		lastUsedVideo = filename;
		System.out.println("Sending done");
      }
	
	private void requestFile(String filename) throws UnknownHostException, IOException {
	    System.out.println("Connecting to " + parentServer + " to request file " + filename);
        File videoFile = new File(localHostName+"/"+filename);
        
        if (new File(localHostName).listFiles().length > 1) {
            if (serverType.equals("reg")) {
                File[] fileList = new File(localHostName).listFiles();
                for (int i=0; i<fileList.length; i++) {
                    if (!fileList[i].getName().equals(lastUsedVideo)) {
                        //remove video from cache
                        fileList[i].delete();
                    }
                }
            }
        }
	    
		Socket requestSocket = new Socket(parentServer, 32000);
		BufferedReader socketInput = new BufferedReader(new InputStreamReader(requestSocket.getInputStream()));



		PrintWriter socketOutput = new PrintWriter(new BufferedWriter(new OutputStreamWriter(requestSocket.getOutputStream())), true);
		socketOutput.println(filename);
		//socketOutput.close();
	    

	    // Recieves the filesize
	    int filesize = -1;
	    String filsiz = null;
	    while(filesize < 0) {
	    	filsiz = socketInput.readLine();
	    	if(filsiz != null) {

	    		filesize = Integer.parseInt(filsiz);
	    		//socketInput.close();
	    	}
	    }
	    

	    // receive file
	    int bytesRead;
	    int current = 0;
	    byte [] mybytearray  = new byte [filesize];

	    InputStream is = requestSocket.getInputStream();
	    String fileName = localHostName +"/"+filename;
	    FileOutputStream fos = new FileOutputStream(fileName);
	    BufferedOutputStream bos = new BufferedOutputStream(fos);
	    bytesRead = is.read(mybytearray,0,mybytearray.length);
	    current = bytesRead;

	    do {
	       bytesRead = is.read(mybytearray, current, (mybytearray.length-current));
	       if(bytesRead >= 0) {
	    	   current += bytesRead;
	       }
	    } while(current < filesize);
	    

	    bos.write(mybytearray, 0 , current);
	    bos.flush();
	    long end = System.currentTimeMillis();
	    bos.close();
	    requestSocket.close();
  }
	
	//------------------------------------
	//main
	//------------------------------------
	public static void main(String argv[]) throws Exception
	{
		//create a Server object
		Server theServer = new Server();
		
		// Starts the FileManager in own thread
		Runnable filemanager = new FileManager(theServer);
		Thread filethread = new Thread(filemanager);
		filethread.start();
		
		// Checks if server has own directory, if not it creates it
		theServer.localHostName = InetAddress.getLocalHost().getCanonicalHostName();
		File directoryTest = new File(theServer.localHostName);
		
		if(!directoryTest.exists()) {
			new File(theServer.localHostName).mkdir();
		}
		
		
		//show GUI:
		theServer.pack();
		theServer.setVisible(true);

		//get RTSP socket port from the command line
		int RTSPport = Integer.parseInt(argv[0]);
	 
		// get the parent node from command line. If parent is not defined, server is master
		if(argv.length < 2 ) {
			theServer.serverType = "root";
			theServer.parentServer = null;
		}
		else {
			theServer.serverType = "reg";
			theServer.parentServer = argv[1];
		}
	 
		//Initiate TCP connection with the client for the RTSP session
		ServerSocket listenSocket = new ServerSocket(RTSPport);
		theServer.RTSPsocket = listenSocket.accept();
		listenSocket.close();

		//Get Client IP address
		theServer.ClientIPAddr = theServer.RTSPsocket.getInetAddress();

		//Initiate RTSPstate
		state = INIT;

		//Set input and output stream filters:
		RTSPBufferedReader = new BufferedReader(new InputStreamReader(theServer.RTSPsocket.getInputStream()) );
		RTSPBufferedWriter = new BufferedWriter(new OutputStreamWriter(theServer.RTSPsocket.getOutputStream()) );

		//Wait for the SETUP message from the client
		int request_type;
		boolean done = false;
		while(!done)
			{
	request_type = theServer.parse_RTSP_request(); //blocking
	
	if (request_type == SETUP)
		{
			done = true;

			//update RTSP state
			state = READY;
			System.out.println("New RTSP state: READY");
	 
			//Send response
			theServer.send_RTSP_response();
	 
			//init the VideoStream object:
			theServer.video = new VideoStream(theServer.localHostName+"/"+VideoFileName);

			//init RTP socket
			theServer.RTPsocket = new DatagramSocket();
		}
			}

		 //loop to handle RTSP requests
		while(true)
			{
	//parse the request
	request_type = theServer.parse_RTSP_request(); //blocking
			
	if ((request_type == PLAY) && (state == READY))
		{
			//send back response
			theServer.send_RTSP_response();
			//start timer
			theServer.timer.start();
			//update state
			state = PLAYING;
			System.out.println("New RTSP state: PLAYING");
		}
	else if ((request_type == PAUSE) && (state == PLAYING))
		{
			//send back response
			theServer.send_RTSP_response();
			//stop timer
			theServer.timer.stop();
			//update state
			state = READY;
			System.out.println("New RTSP state: READY");
		}
	else if (request_type == TEARDOWN)
		{
			//send back response
			theServer.send_RTSP_response();
			//stop timer
			theServer.timer.stop();
			//close sockets
			state = INIT;
			theServer.RTSPsocket = listenSocket.accept();
			listenSocket.close();

			//Get Client IP address
			theServer.ClientIPAddr = theServer.RTSPsocket.getInetAddress();

			//Initiate RTSPstate
			state = INIT;

			//Set input and output stream filters:
			RTSPBufferedReader = new BufferedReader(new InputStreamReader(theServer.RTSPsocket.getInputStream()) );
			RTSPBufferedWriter = new BufferedWriter(new OutputStreamWriter(theServer.RTSPsocket.getOutputStream()) );

			//Wait for the SETUP message from the client

			done = false;
			
			//theServer.RTSPsocket.close();
			//theServer.RTPsocket.close();

			//System.exit(0);
		}
			}
	}


}
